package com.ydd.aspect;

import com.ydd.annotation.AutoFill;
import com.ydd.constant.AutoFillConstant;
import com.ydd.context.BaseContext;
import com.ydd.enumeration.OperationType;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.time.LocalDate;
import java.time.LocalDateTime;


/**
 * 自定义切面，实现公共字段自动填充处理逻辑
 */
@Aspect
@Component
@Slf4j
public class AutoFillAspect {

    /**
     * 切入点表达式：扫描 mapper包下所有类与方法 和 使用注解@AutoFill 的方法进行增强
     */
    @Pointcut("execution(* com.ydd.mapper.*.*(..)) && @annotation(com.ydd.annotation.AutoFill)")
    public void pt(){}

    /**
     * 前置通知 在通知中进行公共字段的赋值
     */
    @Before("pt()")
    public void before(JoinPoint joinPoint){
        // 可以先进行调试，看是否能进入该方法，提前在mapper方法添加AutoFill注解
        log.info("公共字段开始填充：{}",joinPoint);

        //目标：对指定的增强方法获取参数对象，填充公共字段

        //1.通过joinPoint获取方法签名对象（原始方法）
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();//方法签名对象

        //2.通过方法签名对象获取@AutoFill注解对象
        AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);//获得方法上的注解

        //3.获取@AutoFill注解对象的值，便于后面对insert或update做不同的处理
        OperationType operationType = autoFill.value();//获得数据库操作类型

        //4.joinPoint获取原始方法的参数数组
        Object[] args = joinPoint.getArgs();

        //5.判断参数数组是否有效，无效结束
        if (args == null || args.length == 0){
            return;
        }

        //6.获取参数数组下标0的参数对象
        Object entity = args[0];

        //7.准备当前时间和登录人id
        LocalDateTime now = LocalDateTime.now();
        Object currentId = BaseContext.getCurrentId();

        //8.判断@AutoFill注解对象的值如果为OperationType.Insert，填充4个字段
        // 判断当前不同的操作类型，为对应的属性通过反射来赋值
        if (operationType == OperationType.INSERT) {
            try {
                // 为公共字段赋值
                Method setCreateDateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_DATE, LocalDateTime.class);
                Method setUpdateDateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_DATE, LocalDateTime.class);

                //通过反射为对象属性赋值
                // 执行方法反射语法：method.invoke(方法所属的对象,数据值)
                setCreateDateTime.invoke(entity, now);  //employee.setCreateTime(empId)
                setUpdateDateTime.invoke(entity, now);
            } catch (Exception e) {
                e.printStackTrace();
                log.error("公共字段填充失败: {}", e.getMessage());
            }
            //9.否则，填充2个字段
        } else if (operationType == OperationType.UPDATE) {
            // 为2个公共字段赋值
            try {
                Method setUpdateDateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_DATE, LocalDateTime.class);
                Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, String.class);

                // 通过反射为对象赋值
                setUpdateDateTime.invoke(entity, now);
                setUpdateUser.invoke(entity, currentId);
            } catch (Exception e) {
                e.printStackTrace();
                log.error("公共字段填充失败: {}", e.getMessage());
            }
        }
    }
}

